/*
* pervasive debugger
+ * www.cl.cam.ac.uk/netos/pdb
*
* alex ho
* 2004
* university of cambridge computer laboratory
+ *
+ * code adapted originally from kgdb & nemesis
*/
#include <xen/lib.h>
#define BUFMAX 400
-#define PDB_ID_OFFSET 2 /* all threads & domains are positive numbers */
-
-static const char hexchars[]="0123456789abcdef";
+static const char hexchars[] = "0123456789abcdef";
static int remote_debug;
static unsigned char pdb_in_checksum;
static unsigned char pdb_xmit_checksum;
-struct pdb_ctx_element
+/* function pointers in the near future... */
+unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid);
+void pdb_linux_get_values(char *buffer, int length, unsigned long address,
+ int pid, unsigned long cr3);
+
+struct pdb_context
{
- int ctrl;
- int info;
- unsigned long ctrl_cr3;
- unsigned long info_cr3;
+ int valid;
+ int domain;
+ int process;
+ unsigned long ptbr; /* cached page table base register */
};
+struct pdb_context pdb_ctx;
-#define pdb_ctx_count 3
-enum pdb_levels {PDB_LVL_XEN = 0, PDB_LVL_GUESTOS, PDB_LVL_PROCESS};
-struct pdb_ctx_element pdb_ctx[pdb_ctx_count];
-int pdb_level = PDB_LVL_XEN;
+int pdb_continue_thread = 0;
+int pdb_general_thread = 0;
void pdb_put_packet (unsigned char *buffer, int ack);
int pdb_initialized = 0;
+int pdb_page_fault_possible = 0;
+int pdb_page_fault_scratch = 0; /* just a handy variable */
+int pdb_page_fault = 0;
static int pdb_serhnd = -1;
static int pdb_stepping = 0;
return serial_getc(pdb_serhnd);
}
-static volatile int mem_err = 0;
-void set_mem_err (void) /* NOT USED YET... */
-{
- mem_err = 1;
-}
-
-/* These are separate functions so that they are so short and sweet
- that the compiler won't save any registers (if there is a fault
- to mem_fault, they won't get restored, so there better not be any
- saved). */
int
get_char (char *addr)
{
}
else if (strcmp(ptr, "fThreadInfo") == 0)
{
+#ifdef PDB_PAST
struct task_struct *p;
u_long flags;
- int buf_idx = 0;
-
- { /* case pdb_lvl_xen */
- int count = 0;
-
- read_lock_irqsave (&tasklist_lock, flags);
-
- pdb_out_buffer[buf_idx++] = 'm';
- for_each_domain ( p )
- {
- domid_t domain = p->domain + PDB_ID_OFFSET;
+#endif /* PDB_PAST */
- if (count > 0)
- {
- pdb_out_buffer[buf_idx++] = ',';
- }
- if (domain > 15)
- {
- pdb_out_buffer[buf_idx++] = hexchars[domain >> 4];
- }
- pdb_out_buffer[buf_idx++] = hexchars[domain % 16];
- count++;
- }
- pdb_out_buffer[buf_idx++] = 0;
-
- read_unlock_irqrestore(&tasklist_lock, flags);
- }
+ int buf_idx = 0;
-#ifdef PDB_FUTURE
+ pdb_out_buffer[buf_idx++] = 'l';
+ pdb_out_buffer[buf_idx++] = 0;
+#ifdef PDB_PAST
switch (pdb_level)
{
case PDB_LVL_XEN: /* return a list of domains */
int foobar[20];
int loop, total;
- /* *** BUG: this cr3 is wrong wrong wrong */
+ /* this cr3 is wrong! */
total = pdb_linux_process_list(pdb_ctx[pdb_level].info_cr3,
foobar, 20);
default:
break;
}
-
-#endif /* PDB_FUTURE */
+#endif /* PDB_PAST */
}
else if (strcmp(ptr, "sThreadInfo") == 0)
}
else if (strncmp(ptr, "ThreadExtraInfo,", 16) == 0)
{
+ int thread = 0;
+ char *message = "foobar ?";
+
+ ptr += 16;
+ if (hexToInt (&ptr, &thread))
+ {
+ mem2hex (message, pdb_out_buffer, strlen(message) + 1);
+ }
+
+#ifdef PDB_PAST
int thread = 0;
char message[16];
struct task_struct *p;
{
mem2hex ((char *)message, pdb_out_buffer, strlen(message) + 1);
}
+#endif /* PDB_PAST */
#ifdef PDB_FUTURE
{
}
else
{
- printk("pdb_process_query: unknown query [%s]\n", ptr);
+ printk("pdb: error, unknown query [%s]\n", ptr);
}
}
pdb_out_buffer[0] = 0;
- if (pdb_ctx[pdb_level].ctrl_cr3 == 0 &&
- pdb_ctx[pdb_level].ctrl >= 0)
+ if (pdb_ctx.valid == 1)
{
- struct task_struct *p;
+ if (pdb_ctx.domain == -1) /* pdb context: xen */
+ {
+ struct task_struct *p;
- p = find_domain_by_id(pdb_ctx[pdb_level].ctrl);
- if (p->mm.shadow_mode)
- pdb_ctx[pdb_level].ctrl_cr3 = pagetable_val(p->mm.shadow_table);
- else
- pdb_ctx[pdb_level].ctrl_cr3 = pagetable_val(p->mm.pagetable);
- put_task_struct(p);
+ p = &idle0_task;
+ if (p->mm.shadow_mode)
+ pdb_ctx.ptbr = pagetable_val(p->mm.shadow_table);
+ else
+ pdb_ctx.ptbr = pagetable_val(p->mm.pagetable);
+ }
+ else if (pdb_ctx.process == -1) /* pdb context: guest os */
+ {
+ struct task_struct *p;
- TRC(printk ("PROCESS: PDB SET CONTROL DOMAIN TO 0x%lx 0x%x\n",
- pdb_ctx[pdb_level].ctrl_cr3,
- pdb_ctx[pdb_level].ctrl));
- }
- if (pdb_ctx[pdb_level].info_cr3 == 0 &&
- pdb_ctx[pdb_level].info >= 0)
- {
- struct task_struct *p;
+ p = find_domain_by_id(pdb_ctx.domain);
+ if (p->mm.shadow_mode)
+ pdb_ctx.ptbr = pagetable_val(p->mm.shadow_table);
+ else
+ pdb_ctx.ptbr = pagetable_val(p->mm.pagetable);
+ put_task_struct(p);
+ }
+ else /* pdb context: process */
+ {
+ struct task_struct *p;
+ unsigned long domain_ptbr;
- p = find_domain_by_id(pdb_ctx[pdb_level].info);
- if (p->mm.shadow_mode)
- pdb_ctx[pdb_level].info_cr3 = pagetable_val(p->mm.shadow_table);
- else
- pdb_ctx[pdb_level].info_cr3 = pagetable_val(p->mm.pagetable);
- put_task_struct(p);
- TRC(printk ("PROCESS: PDB SET INFO DOMAIN TO 0x%lx 0x%x\n",
- pdb_ctx[pdb_level].info_cr3,
- pdb_ctx[pdb_level].info));
+ p = find_domain_by_id(pdb_ctx.domain);
+ if (p->mm.shadow_mode)
+ domain_ptbr = pagetable_val(p->mm.shadow_table);
+ else
+ domain_ptbr = pagetable_val(p->mm.pagetable);
+ put_task_struct(p);
+
+ pdb_ctx.ptbr = domain_ptbr;
+ /* pdb_ctx.ptbr = pdb_linux_pid_ptbr(domain_ptbr, pdb_ctx.process); */
+ }
+
+ pdb_ctx.valid = 0;
+ TRC(printk ("pdb change context (dom:%d, proc:%d) now 0x%lx\n",
+ pdb_ctx.domain, pdb_ctx.process, pdb_ctx.ptbr));
}
switch (*ptr++)
if (hexToInt (&next, &thread))
{
- if (thread > 0)
- {
- thread = thread - PDB_ID_OFFSET;
- }
if (*ptr == 'c')
{
- pdb_ctx[pdb_level].ctrl = thread;
-
- if (thread > 0)
- {
- struct task_struct *p = find_domain_by_id(thread);
- if (p->mm.shadow_mode)
- pdb_ctx[pdb_level].ctrl_cr3 = pagetable_val(p->mm.shadow_table);
- else
- pdb_ctx[pdb_level].ctrl_cr3 = pagetable_val(p->mm.pagetable);
- put_task_struct(p);
- TRC(printk ("PDB SET CONTROL DOMAIN TO 0x%lx 0x%x\n",
- pdb_ctx[pdb_level].ctrl_cr3,
- pdb_ctx[pdb_level].ctrl));
- }
+ pdb_continue_thread = thread;
}
else if (*ptr == 'g')
{
- pdb_ctx[pdb_level].info = thread;
-
- if (thread > 0)
- {
- struct task_struct *p = find_domain_by_id(thread);
- if (p->mm.shadow_mode)
- pdb_ctx[pdb_level].info_cr3 = pagetable_val(p->mm.shadow_table);
- else
- pdb_ctx[pdb_level].info_cr3 = pagetable_val(p->mm.pagetable);
- put_task_struct(p);
- TRC(printk ("PDB SET INFO DOMAIN TO 0x%lx 0x%x\n",
- pdb_ctx[pdb_level].info_cr3,
- pdb_ctx[pdb_level].info));
- }
+ pdb_general_thread = thread;
}
else
{
- printk ("ack, unknown command %c (thread: %d)\n",
+ printk ("pdb error: unknown set thread command %c (%d)\n",
*ptr, thread);
+ strcpy (pdb_out_buffer, "E00");
+ break;
}
}
strcpy (pdb_out_buffer, "OK");
if (hexToInt (&ptr, &length))
{
ptr = 0;
- mem_err = 0;
- if (pdb_ctx[pdb_level].info >= 0)
+ pdb_page_fault_possible = 1;
+ pdb_page_fault = 0;
+ if (addr >= PAGE_OFFSET)
+ {
+ mem2hex ((char *) addr, pdb_out_buffer, length);
+ }
+ else if (pdb_ctx.process != -1)
+ {
+ pdb_linux_get_values(pdb_buffer, length, addr,
+ pdb_ctx.process, pdb_ctx.ptbr);
+ mem2hex (pdb_buffer, pdb_out_buffer, length);
+ }
+ else
{
- pdb_get_values(pdb_buffer, length,
- pdb_ctx[pdb_level].info_cr3, addr);
+ pdb_get_values (pdb_buffer, length,
+ pdb_ctx.ptbr, addr);
mem2hex (pdb_buffer, pdb_out_buffer, length);
}
- else
- mem2hex ((char *) addr, pdb_out_buffer, length);
- if (mem_err)
- {
+
+ pdb_page_fault_possible = 0;
+ if (pdb_page_fault)
+ {
strcpy (pdb_out_buffer, "E03");
- }
+ }
}
if (ptr)
if (hexToInt (&ptr, &length))
if (*(ptr++) == ':')
{
- mem_err = 0;
-
- /* pdb_set_values(ptr, length, cr3, addr); */
- pdb_set_values(ptr, length,
- pdb_ctx[pdb_level].info_cr3, addr);
- if (mem_err)
+ pdb_page_fault_possible = 1;
+ pdb_page_fault = 0;
+ if (addr >= PAGE_OFFSET)
+ {
+ hex2mem (ptr, (char *)addr, length);
+ }
+ else
+ {
+ pdb_set_values (ptr, length,
+ pdb_ctx.ptbr, addr);
+ }
+ pdb_page_fault_possible = 0;
+ if (pdb_page_fault)
{
strcpy (pdb_out_buffer, "E03");
}
if (hexToInt (&ptr, &id))
{
- { /* case pdb_lvl_xen */
- struct task_struct *p;
- id -= PDB_ID_OFFSET;
- if ( (p = find_domain_by_id(id)) == NULL)
- strcpy (pdb_out_buffer, "E00");
- else
- strcpy (pdb_out_buffer, "OK");
- put_task_struct(p);
- }
+ strcpy (pdb_out_buffer, "E00");
-#ifdef PDB_FUTURE
+#ifdef PDB_PAST
switch (pdb_level) /* previous level */
{
}
}
-#endif /* PDB_FUTURE */
+#endif /* PDB_PAST */
}
break;
}
l2_table += l2_table_offset(addr);
if (!(l2_pgentry_val(*l2_table) & _PAGE_PRESENT))
{
- struct task_struct *p = find_domain_by_id(0);
- printk ("pdb error: cr3: 0x%lx dom0cr3: 0x%lx\n", cr3,
- p->mm.shadow_mode ? pagetable_val(p->mm.shadow_table)
- : pagetable_val(p->mm.pagetable));
- put_task_struct(p);
-
- printk ("pdb error: L2:0x%p (0x%lx) \n",
- l2_table, l2_pgentry_val(*l2_table));
+ if (pdb_page_fault_possible == 1)
+ {
+ pdb_page_fault = 1;
+ TRC(printk("pdb: L2 error (0x%lx)\n", addr));
+ }
+ else
+ {
+ struct task_struct *p = find_domain_by_id(0);
+ printk ("pdb error: cr3: 0x%lx dom0cr3: 0x%lx\n", cr3,
+ p->mm.shadow_mode ? pagetable_val(p->mm.shadow_table)
+ : pagetable_val(p->mm.pagetable));
+ put_task_struct(p);
+ printk ("pdb error: L2:0x%p (0x%lx)\n",
+ l2_table, l2_pgentry_val(*l2_table));
+ }
goto exit2;
}
l1_table += l1_table_offset(addr);
if (!(l1_pgentry_val(*l1_table) & _PAGE_PRESENT))
{
- printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n",
- l2_table, l2_pgentry_val(*l2_table),
- l1_table, l1_pgentry_val(*l1_table));
+ if (pdb_page_fault_possible == 1)
+ {
+ pdb_page_fault = 1;
+ TRC(printk ("pdb: L1 error (0x%lx)\n", addr));
+ }
+ else
+ {
+ printk ("L2:0x%p (0x%lx) L1:0x%p (0x%lx)\n",
+ l2_table, l2_pgentry_val(*l2_table),
+ l1_table, l1_pgentry_val(*l1_table));
+ }
goto exit1;
}
memcpy (buffer, page, length);
bytes = length;
break;
- case __PDB_SET_VAL: /* write */
+ case __PDB_SET_VAL: /* write */
hex2mem (buffer, page, length);
bytes = length;
break;
- default: /* unknown */
+ default: /* unknown */
printk ("error: unknown RW flag: %d\n", rw);
return 0;
}
xen_regs->eip--;
}
- watchdog_save = watchdog_on;
- watchdog_on = 0;
-
/* Generate a signal for GDB. */
switch ( exceptionVector )
{
pdb_out_buffer[3] = 0;
pdb_put_packet(pdb_out_buffer, 1);
+ watchdog_save = watchdog_on;
+ watchdog_on = 0;
+
do {
pdb_out_buffer[0] = 0;
pdb_get_packet(pdb_in_buffer);
void initialize_pdb()
{
extern char opt_pdb[];
- int loop;
/* Certain state must be initialised even when PDB will not be used. */
memset((void *) &breakpoints, 0, sizeof(breakpoints));
return;
}
- for (loop = 0; loop < pdb_ctx_count; loop++)
- {
- pdb_ctx[loop].ctrl = -1;
- pdb_ctx[loop].info = -1;
- pdb_ctx[loop].ctrl_cr3 = 0;
- pdb_ctx[loop].info_cr3 = 0;
- }
+ pdb_ctx.valid = 1;
+ pdb_ctx.domain = -1;
+ pdb_ctx.process = -1;
+ pdb_ctx.ptbr = 0;
- printk("Initialized pervasive debugger (PDB) on port %s\n", opt_pdb);
+ printk("pdb: pervasive debugger (%s) www.cl.cam.ac.uk/netos/pdb\n",
+ opt_pdb);
/* Acknowledge any spurious GDB packets. */
pdb_put_char('+');
+
+/*
+ * pervasive debugger
+ * www.cl.cam.ac.uk/netos/pdb
+ *
+ * alex ho
+ * 2004
+ * university of cambridge computer laboratory
+ *
+ * linux specific pdb stuff
+ */
+
#include <xen/config.h>
#include <xen/types.h>
#include <xen/lib.h>
#include <hypervisor-ifs/dom0_ops.h>
#include <asm/pdb.h>
-/*
- * linux specific pdb stuff
- */
-
/* from linux/sched.h */
#define PIDHASH_SZ (4096 >> 2)
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
#define ENTRIES_PER_L1_PAGETABLE 1024
#define L1_PAGE_BITS ( (ENTRIES_PER_L1_PAGETABLE - 1) << PAGE_SHIFT )
-
void pdb_linux_process_details (unsigned long cr3, int pid, char *buffer);
-
/* adapted from asm-xen/page.h */
static inline unsigned long machine_to_phys(unsigned long cr3,
unsigned long machine)
return phys;
}
-
-#define pidhash_addr 0xc01971e0UL
-#define init_task_union_addr 0xc0182000UL
+unsigned long pdb_pidhash_addr = 0xc01971e0UL;
+unsigned long pdb_init_task_union_addr = 0xc0182000UL;
#define task_struct_mm_offset 0x2c
#define task_struct_next_task_offset 0x48
#define mm_struct_pgd_offset 0x0c
-/* read a byte from a process */
-u_char pdb_linux_get_value(int pid, unsigned long cr3, unsigned long addr)
+/*
+ * find the task structure of a process (pid)
+ * given the cr3 of the guest os.
+ */
+unsigned long pdb_linux_pid_task_struct (unsigned long cr3, int pid)
{
- u_char result = 0;
- unsigned long task_struct_p, mm_p, pgd, task_struct_pid;
- unsigned long l2tab, page;
+ unsigned long task_struct_p = (unsigned long) NULL;
+ unsigned long task_struct_pid;
/* find the task_struct of the given process */
pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p),
- cr3, pidhash_addr + pid_hashfn(pid) * 4);
+ cr3, pdb_pidhash_addr + pid_hashfn(pid) * 4);
/* find the correct task struct */
while (task_struct_p != (unsigned long)NULL)
{
break;
}
-
+
pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p),
cr3, task_struct_p + task_struct_pidhash_next_offset);
}
- if (task_struct_p == (unsigned long)NULL)
+ if (task_struct_p == (unsigned long) NULL)
{
/* oops */
- printk ("error: pdb couldn't find process 0x%x\n", pid);
- return 0;
+ printk ("pdb error: couldn't find process 0x%x (0x%lx)\n", pid, cr3);
+ }
+
+ return task_struct_p;
+}
+
+/*
+ * find the ptbr of a process (pid)
+ * given the cr3 of the guest os.
+ */
+unsigned long pdb_linux_pid_ptbr (unsigned long cr3, int pid)
+{
+ unsigned long task_struct_p;
+ unsigned long mm_p, pgd;
+
+ task_struct_p = pdb_linux_pid_task_struct(cr3, pid);
+ if (task_struct_p == (unsigned long) NULL)
+ {
+ return (unsigned long) NULL;
}
/* get the mm_struct within the task_struct */
pdb_get_values((u_char *) &pgd, sizeof(pgd),
cr3, mm_p + mm_struct_pgd_offset);
+ return pgd;
+}
+
+
+
+/* read a byte from a process
+ *
+ * in: pid: process id
+ * cr3: ptbr for the process' domain
+ * addr: address to read
+ */
+
+u_char pdb_linux_get_value(int pid, unsigned long cr3, unsigned long addr)
+{
+ u_char result = 0;
+ unsigned long pgd;
+ unsigned long l2tab, page;
+
+ /* get the process' pgd */
+ pgd = pdb_linux_pid_ptbr(cr3, pid);
+
/* get the l2 table entry */
pdb_get_values((u_char *) &l2tab, sizeof(l2tab),
cr3, pgd + (addr >> PGDIR_SHIFT) * 4);
return result;
}
-/* return 1 if is the virtual address is in the operating system's
- address space, else 0 */
+void pdb_linux_get_values(char *buffer, int length, unsigned long address,
+ int pid, unsigned long cr3)
+{
+ int loop;
+
+ /* yes, this can be optimized... a lot */
+ for (loop = 0; loop < length; loop++)
+ {
+ buffer[loop] = pdb_linux_get_value(pid, cr3, address + loop);
+ }
+}
+
+/*
+ * return 1 if is the virtual address is in the operating system's
+ * address space, else 0
+ */
int pdb_linux_address_space (unsigned long addr)
{
return (addr > PAGE_OFFSET);
/* task_p = init_task->next_task */
pdb_get_values((u_char *) &task_p, sizeof(task_p),
- cr3, init_task_union_addr + task_struct_next_task_offset);
+ cr3, pdb_init_task_union_addr + task_struct_next_task_offset);
- while (task_p != init_task_union_addr)
+ while (task_p != pdb_init_task_union_addr)
{
pdb_get_values((u_char *) &pid, sizeof(pid),
cr3, task_p + task_struct_pid_offset);
return count;
}
-/* get additional details about a particular process:
+/*
+ * get additional details about a particular process
*/
void pdb_linux_process_details (unsigned long cr3, int pid, char *buffer)
{
- unsigned long task_struct_p, task_struct_pid;
-
- /* find the task_struct of the given process */
- pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p),
- cr3, pidhash_addr + pid_hashfn(pid) * 4);
+ unsigned long task_struct_p;
- /* find the correct task struct */
- while (task_struct_p != (unsigned long)NULL)
- {
- pdb_get_values((u_char *) &task_struct_pid, sizeof(task_struct_pid),
- cr3, task_struct_p + task_struct_pid_offset);
- if (task_struct_pid == pid)
- {
- break;
- }
-
- pdb_get_values((u_char *) &task_struct_p, sizeof(task_struct_p),
- cr3, task_struct_p + task_struct_pidhash_next_offset);
- }
- if (task_struct_p == (unsigned long)NULL)
- {
- /* oops */
- printk ("error: pdb couldn't find process 0x%x\n", pid);
- return;
- }
+ task_struct_p = pdb_linux_pid_task_struct(cr3, pid);
pdb_get_values((u_char *) buffer, task_struct_comm_length,
cr3, task_struct_p + task_struct_comm_offset);